"""
代码搜索工具模块

提供代码搜索、文本搜索、模式匹配等功能。
"""

import re
import os
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, List, Optional, Union

from .base import FileTool, ToolResult, ToolError


class CodeSearchTool(FileTool):
    """代码搜索工具"""
    
    def __init__(self):
        super().__init__(
            name="code_search",
            description="在代码文件中搜索文本内容",
            version="1.0.0"
        )
        self.supported_extensions = ['.py', '.js', '.ts', '.java', '.cpp', '.c', '.cs', '.php', '.rb', '.go', '.rs', '.html', '.css', '.json', '.xml', '.yaml', '.yml', '.md', '.txt']
    
    def get_parameters(self) -> Dict[str, Dict[str, Any]]:
        return {
            'query': {
                'type': str,
                'required': True,
                'description': '搜索查询文本'
            },
            'directory': {
                'type': str,
                'required': False,
                'default': '.',
                'description': '搜索目录（默认当前目录）'
            },
            'file_types': {
                'type': list,
                'required': False,
                'default': ['.py', '.js', '.ts', '.java', '.cpp', '.c', '.cs', '.php', '.rb', '.go', '.rs'],
                'description': '要搜索的文件类型'
            },
            'case_sensitive': {
                'type': bool,
                'required': False,
                'default': False,
                'description': '是否区分大小写'
            },
            'whole_word': {
                'type': bool,
                'required': False,
                'default': False,
                'description': '是否匹配完整单词'
            },
            'regex': {
                'type': bool,
                'required': False,
                'default': False,
                'description': '是否使用正则表达式'
            },
            'exclude_dirs': {
                'type': list,
                'required': False,
                'default': ['.git', '__pycache__', 'node_modules', '.venv', 'venv', 'env', '.env'],
                'description': '排除的目录'
            },
            'max_results': {
                'type': int,
                'required': False,
                'default': 100,
                'min': 1,
                'max': 1000,
                'description': '最大结果数量'
            },
            'context_lines': {
                'type': int,
                'required': False,
                'default': 2,
                'min': 0,
                'max': 10,
                'description': '显示上下文行数'
            }
        }
    
    def execute(self, **kwargs) -> ToolResult:
        """执行代码搜索"""
        start_time = datetime.now()
        
        try:
            if not self.validate_parameters(**kwargs):
                return self._create_result(False, error="参数验证失败")
            
            query = kwargs['query']
            directory = Path(kwargs.get('directory', '.'))
            file_types = kwargs.get('file_types', ['.py', '.js', '.ts', '.java', '.cpp', '.c', '.cs', '.php', '.rb', '.go', '.rs'])
            case_sensitive = kwargs.get('case_sensitive', False)
            whole_word = kwargs.get('whole_word', False)
            regex = kwargs.get('regex', False)
            exclude_dirs = kwargs.get('exclude_dirs', ['.git', '__pycache__', 'node_modules', '.venv', 'venv', 'env', '.env'])
            max_results = kwargs.get('max_results', 100)
            context_lines = kwargs.get('context_lines', 2)
            
            # 验证目录
            self._validate_directory_path(directory)
            
            # 构建搜索模式
            search_pattern = self._build_search_pattern(query, case_sensitive, whole_word, regex)
            
            # 搜索文件
            results = self._search_in_files(
                directory, search_pattern, file_types, exclude_dirs, 
                max_results, context_lines
            )
            
            execution_time = (datetime.now() - start_time).total_seconds()
            
            return self._create_result(
                success=True,
                data={
                    'results': results,
                    'total_matches': sum(len(r['matches']) for r in results),
                    'files_searched': len(results),
                    'query': query
                },
                message=f"在 {len(results)} 个文件中找到 {sum(len(r['matches']) for r in results)} 个匹配项",
                execution_time=execution_time,
                metadata={
                    'search_params': {
                        'case_sensitive': case_sensitive,
                        'whole_word': whole_word,
                        'regex': regex,
                        'file_types': file_types,
                        'exclude_dirs': exclude_dirs
                    }
                }
            )
            
        except Exception as e:
            execution_time = (datetime.now() - start_time).total_seconds()
            return self._create_result(
                success=False,
                error=str(e),
                execution_time=execution_time
            )
    
    def _build_search_pattern(self, query: str, case_sensitive: bool, 
                            whole_word: bool, regex: bool) -> re.Pattern:
        """构建搜索模式"""
        if regex:
            flags = 0 if case_sensitive else re.IGNORECASE
            return re.compile(query, flags)
        else:
            # 转义特殊字符
            escaped_query = re.escape(query)
            
            # 完整单词匹配
            if whole_word:
                escaped_query = r'\b' + escaped_query + r'\b'
            
            flags = 0 if case_sensitive else re.IGNORECASE
            return re.compile(escaped_query, flags)
    
    def _search_in_files(self, directory: Path, pattern: re.Pattern, 
                        file_types: List[str], exclude_dirs: List[str],
                        max_results: int, context_lines: int) -> List[Dict[str, Any]]:
        """在文件中搜索"""
        results = []
        total_matches = 0
        
        for file_path in directory.rglob('*'):
            if total_matches >= max_results:
                break
            
            # 跳过目录
            if not file_path.is_file():
                continue
            
            # 检查文件类型
            if file_types and file_path.suffix not in file_types:
                continue
            
            # 检查排除目录
            if any(part in exclude_dirs for part in file_path.parts):
                continue
            
            # 搜索文件内容
            matches = self._search_in_file(file_path, pattern, context_lines)
            if matches:
                results.append({
                    'file': str(file_path),
                    'relative_path': str(file_path.relative_to(directory)),
                    'matches': matches
                })
                total_matches += len(matches)
        
        return results
    
    def _search_in_file(self, file_path: Path, pattern: re.Pattern, 
                       context_lines: int) -> List[Dict[str, Any]]:
        """在单个文件中搜索"""
        matches = []
        
        try:
            with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
                lines = f.readlines()
            
            for line_num, line in enumerate(lines, 1):
                if pattern.search(line):
                    # 获取上下文
                    start_line = max(1, line_num - context_lines)
                    end_line = min(len(lines), line_num + context_lines + 1)
                    
                    context = []
                    for i in range(start_line - 1, end_line):
                        context.append({
                            'line_number': i + 1,
                            'content': lines[i].rstrip('\n'),
                            'is_match': i + 1 == line_num
                        })
                    
                    matches.append({
                        'line_number': line_num,
                        'content': line.rstrip('\n'),
                        'context': context
                    })
        
        except (UnicodeDecodeError, PermissionError, OSError) as e:
            self._log_warning(f"无法读取文件 {file_path}: {e}")
        
        return matches


class TextSearchTool(FileTool):
    """文本搜索工具"""
    
    def __init__(self):
        super().__init__(
            name="text_search",
            description="在文本文件中搜索内容",
            version="1.0.0"
        )
        self.supported_extensions = ['.txt', '.md', '.rst', '.log', '.csv', '.json', '.xml', '.yaml', '.yml']
    
    def get_parameters(self) -> Dict[str, Dict[str, Any]]:
        return {
            'query': {
                'type': str,
                'required': True,
                'description': '搜索查询文本'
            },
            'files': {
                'type': list,
                'required': False,
                'description': '要搜索的文件列表（空则搜索目录）'
            },
            'directory': {
                'type': str,
                'required': False,
                'default': '.',
                'description': '搜索目录'
            },
            'recursive': {
                'type': bool,
                'required': False,
                'default': True,
                'description': '是否递归搜索子目录'
            },
            'case_sensitive': {
                'type': bool,
                'required': False,
                'default': False,
                'description': '是否区分大小写'
            },
            'whole_word': {
                'type': bool,
                'required': False,
                'default': False,
                'description': '是否匹配完整单词'
            },
            'regex': {
                'type': bool,
                'required': False,
                'default': False,
                'description': '是否使用正则表达式'
            },
            'max_results': {
                'type': int,
                'required': False,
                'default': 50,
                'min': 1,
                'max': 500,
                'description': '最大结果数量'
            }
        }
    
    def execute(self, **kwargs) -> ToolResult:
        """执行文本搜索"""
        start_time = datetime.now()
        
        try:
            if not self.validate_parameters(**kwargs):
                return self._create_result(False, error="参数验证失败")
            
            query = kwargs['query']
            files = kwargs.get('files', [])
            directory = Path(kwargs.get('directory', '.'))
            recursive = kwargs.get('recursive', True)
            case_sensitive = kwargs.get('case_sensitive', False)
            whole_word = kwargs.get('whole_word', False)
            regex = kwargs.get('regex', False)
            max_results = kwargs.get('max_results', 50)
            
            # 构建搜索模式
            search_pattern = self._build_search_pattern(query, case_sensitive, whole_word, regex)
            
            # 确定要搜索的文件
            if files:
                file_paths = [Path(f) for f in files]
            else:
                file_paths = self._get_text_files(directory, recursive)
            
            # 搜索文件
            results = []
            total_matches = 0
            
            for file_path in file_paths:
                if total_matches >= max_results:
                    break
                
                matches = self._search_in_file(file_path, search_pattern)
                if matches:
                    results.append({
                        'file': str(file_path),
                        'matches': matches
                    })
                    total_matches += len(matches)
            
            execution_time = (datetime.now() - start_time).total_seconds()
            
            return self._create_result(
                success=True,
                data={
                    'results': results,
                    'total_matches': total_matches,
                    'files_searched': len(file_paths)
                },
                message=f"在 {len(results)} 个文件中找到 {total_matches} 个匹配项",
                execution_time=execution_time
            )
            
        except Exception as e:
            execution_time = (datetime.now() - start_time).total_seconds()
            return self._create_result(
                success=False,
                error=str(e),
                execution_time=execution_time
            )
    
    def _build_search_pattern(self, query: str, case_sensitive: bool, 
                            whole_word: bool, regex: bool) -> re.Pattern:
        """构建搜索模式"""
        if regex:
            flags = 0 if case_sensitive else re.IGNORECASE
            return re.compile(query, flags)
        else:
            escaped_query = re.escape(query)
            if whole_word:
                escaped_query = r'\b' + escaped_query + r'\b'
            flags = 0 if case_sensitive else re.IGNORECASE
            return re.compile(escaped_query, flags)
    
    def _get_text_files(self, directory: Path, recursive: bool) -> List[Path]:
        """获取文本文件列表"""
        files = []
        
        try:
            if recursive:
                for file_path in directory.rglob('*'):
                    if file_path.is_file() and file_path.suffix in self.supported_extensions:
                        files.append(file_path)
            else:
                for file_path in directory.iterdir():
                    if file_path.is_file() and file_path.suffix in self.supported_extensions:
                        files.append(file_path)
        except PermissionError:
            pass
        
        return files
    
    def _search_in_file(self, file_path: Path, pattern: re.Pattern) -> List[Dict[str, Any]]:
        """在单个文件中搜索"""
        matches = []
        
        try:
            with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
                lines = f.readlines()
            
            for line_num, line in enumerate(lines, 1):
                if pattern.search(line):
                    matches.append({
                        'line_number': line_num,
                        'content': line.rstrip('\n')
                    })
        
        except (UnicodeDecodeError, PermissionError, OSError):
            pass
        
        return matches


class PatternSearchTool(FileTool):
    """模式搜索工具"""
    
    def __init__(self):
        super().__init__(
            name="pattern_search",
            description="使用预定义模式搜索代码",
            version="1.0.0"
        )
        self.supported_extensions = ['.py', '.js', '.ts', '.java', '.cpp', '.c', '.cs', '.php', '.rb', '.go', '.rs']
    
    def get_parameters(self) -> Dict[str, Dict[str, Any]]:
        return {
            'pattern_type': {
                'type': str,
                'required': True,
                'choices': ['function', 'class', 'import', 'variable', 'comment', 'todo', 'fixme', 'custom'],
                'description': '搜索模式类型'
            },
            'custom_pattern': {
                'type': str,
                'required': False,
                'description': '自定义正则表达式模式（当 pattern_type 为 custom 时）'
            },
            'directory': {
                'type': str,
                'required': False,
                'default': '.',
                'description': '搜索目录'
            },
            'file_types': {
                'type': list,
                'required': False,
                'default': ['.py', '.js', '.ts', '.java', '.cpp', '.c', '.cs', '.php', '.rb', '.go', '.rs'],
                'description': '要搜索的文件类型'
            },
            'case_sensitive': {
                'type': bool,
                'required': False,
                'default': False,
                'description': '是否区分大小写'
            },
            'max_results': {
                'type': int,
                'required': False,
                'default': 100,
                'min': 1,
                'max': 1000,
                'description': '最大结果数量'
            }
        }
    
    def execute(self, **kwargs) -> ToolResult:
        """执行模式搜索"""
        start_time = datetime.now()
        
        try:
            if not self.validate_parameters(**kwargs):
                return self._create_result(False, error="参数验证失败")
            
            pattern_type = kwargs['pattern_type']
            custom_pattern = kwargs.get('custom_pattern')
            directory = Path(kwargs.get('directory', '.'))
            file_types = kwargs.get('file_types', ['.py', '.js', '.ts', '.java', '.cpp', '.c', '.cs', '.php', '.rb', '.go', '.rs'])
            case_sensitive = kwargs.get('case_sensitive', False)
            max_results = kwargs.get('max_results', 100)
            
            # 获取搜索模式
            if pattern_type == 'custom':
                if not custom_pattern:
                    return self._create_result(False, error="自定义模式不能为空")
                pattern = custom_pattern
            else:
                pattern = self._get_predefined_pattern(pattern_type)
            
            # 构建正则表达式
            flags = 0 if case_sensitive else re.IGNORECASE
            regex_pattern = re.compile(pattern, flags)
            
            # 搜索文件
            results = self._search_patterns_in_files(
                directory, regex_pattern, file_types, max_results
            )
            
            execution_time = (datetime.now() - start_time).total_seconds()
            
            return self._create_result(
                success=True,
                data={
                    'results': results,
                    'total_matches': sum(len(r['matches']) for r in results),
                    'pattern_type': pattern_type,
                    'pattern': pattern
                },
                message=f"找到 {sum(len(r['matches']) for r in results)} 个 {pattern_type} 匹配项",
                execution_time=execution_time
            )
            
        except Exception as e:
            execution_time = (datetime.now() - start_time).total_seconds()
            return self._create_result(
                success=False,
                error=str(e),
                execution_time=execution_time
            )
    
    def _get_predefined_pattern(self, pattern_type: str) -> str:
        """获取预定义模式"""
        patterns = {
            'function': r'(?:def|function|func)\s+(\w+)\s*\(',
            'class': r'class\s+(\w+)',
            'import': r'(?:import|from)\s+[\w\.]+',
            'variable': r'(?:let|var|const|int|string|float|double|bool)\s+(\w+)',
            'comment': r'//.*|/\*.*?\*/|#.*',
            'todo': r'(?:TODO|FIXME|HACK|NOTE|XXX):\s*(.+)',
            'fixme': r'FIXME:\s*(.+)'
        }
        
        return patterns.get(pattern_type, '')
    
    def _search_patterns_in_files(self, directory: Path, pattern: re.Pattern,
                                 file_types: List[str], max_results: int) -> List[Dict[str, Any]]:
        """在文件中搜索模式"""
        results = []
        total_matches = 0
        
        for file_path in directory.rglob('*'):
            if total_matches >= max_results:
                break
            
            if not file_path.is_file() or file_path.suffix not in file_types:
                continue
            
            matches = self._search_pattern_in_file(file_path, pattern)
            if matches:
                results.append({
                    'file': str(file_path),
                    'relative_path': str(file_path.relative_to(directory)),
                    'matches': matches
                })
                total_matches += len(matches)
        
        return results
    
    def _search_pattern_in_file(self, file_path: Path, pattern: re.Pattern) -> List[Dict[str, Any]]:
        """在单个文件中搜索模式"""
        matches = []
        
        try:
            with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
                lines = f.readlines()
            
            for line_num, line in enumerate(lines, 1):
                match = pattern.search(line)
                if match:
                    matches.append({
                        'line_number': line_num,
                        'content': line.rstrip('\n'),
                        'match_text': match.group(0),
                        'groups': match.groups() if match.groups() else []
                    })
        
        except (UnicodeDecodeError, PermissionError, OSError):
            pass
        
        return matches
